/*
 * ! ******************************************************************************
 *
 * Pentaho Data Integration
 *
 * Copyright (C) 2002-2017 by Hitachi Vantara : http://www.pentaho.com
 *
 *******************************************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 *
 ******************************************************************************/

package org.pentaho.di.ui.core.dialog;

import java.text.SimpleDateFormat;
import java.util.*;

import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;
import org.pentaho.di.ExecutionConfiguration;
import org.pentaho.di.base.AbstractMeta;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.Props;
import org.pentaho.di.core.parameters.UnknownParamException;
import org.pentaho.di.core.util.Utils;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.ui.core.PropsUI;
import org.pentaho.di.ui.core.gui.GUIResource;
import org.pentaho.di.ui.core.gui.WindowProperty;
import org.pentaho.di.ui.core.widget.ColumnInfo;
import org.pentaho.di.ui.core.widget.TableView;
import org.pentaho.di.ui.util.HelpUtils;

public abstract class ConfigurationDialog extends Dialog {

    protected AbstractMeta abstractMeta;
    protected ExecutionConfiguration configuration;
    protected TableView wVariables;
    protected boolean retval;
    protected Shell shell;
    protected PropsUI props;
    protected Button wGatherMetrics;
    protected Label wlLogLevel;
    protected Group gDetails;
    protected CCombo wLogLevel;
    protected Button wSafeMode;
    protected Button wClearLog;
    protected Label wlRemoteHost;
    protected CCombo wRemoteHost;
    protected Button wPassExport;
    protected Composite localOptionsComposite;
    protected Composite serverOptionsComposite;
    protected Label environmentSeparator;
    protected StackLayout stackedLayout;
    protected int margin = Const.MARGIN;
    protected Group gLocal;
    protected Composite composite;
    protected Composite cRunConfiguration;
    protected CCombo wRunConfiguration;

    private TableView wParams;
    private Display display;
    private Shell parent;
    private Button wOK;
    private Button wCancel;
    protected FormData fdDetails;
    private FormData fd_tabFolder;
    private CTabFolder tabFolder;
    private Button alwaysShowOption;

    private static Class<?> PKG;
    private static String PREFIX;
    private String cron = "";

    public ConfigurationDialog(Shell parent, ExecutionConfiguration configuration, AbstractMeta meta) {
        super(parent);
        this.parent = parent;
        this.configuration = configuration;
        this.abstractMeta = meta;

        // Fill the parameters, maybe do this in another place?
        Map<String, String> params = configuration.getParams();
        params.clear();
        String[] paramNames = meta.listParameters();
        for (String name : paramNames) {
            params.put(name, "");
        }

        props = PropsUI.getInstance();
    }

    protected void getInfoVariables() {
        Map<String, String> map = new HashMap<String, String>();
        int nrNonEmptyVariables = wVariables.nrNonEmpty();
        for (int i = 0; i < nrNonEmptyVariables; i++) {
            TableItem tableItem = wVariables.getNonEmpty(i);
            String varName = tableItem.getText(1);
            String varValue = tableItem.getText(2);

            if (!Utils.isEmpty(varName)) {
                map.put(varName, varValue);
            }
        }
        configuration.setVariables(map);
    }

    /**
     * Get the parameters from the dialog.
     */
    protected void getInfoParameters() {
        Map<String, String> map = new HashMap<String, String>();
        int nrNonEmptyVariables = wParams.nrNonEmpty();
        for (int i = 0; i < nrNonEmptyVariables; i++) {
            TableItem tableItem = wParams.getNonEmpty(i);
            String paramName = tableItem.getText(1);
            String defaultValue = tableItem.getText(2);
            String paramValue = tableItem.getText(3);

            if (Utils.isEmpty(paramValue)) {
                paramValue = Const.NVL(defaultValue, "");
            }

            map.put(paramName, paramValue);
        }
        configuration.setParams(map);
    }

    protected void ok() {
        abstractMeta.setAlwaysShowRunOptions(alwaysShowOption.getSelection());
        abstractMeta.setShowDialog(alwaysShowOption.getSelection());
        if (Const.isOSX()) {
            // OSX bug workaround.
            wVariables.applyOSXChanges();
            wParams.applyOSXChanges();
        }
        getInfo();
        retval = true;
        dispose();
    }

    private void dispose() {
        props.setScreen(new WindowProperty(shell));
        shell.dispose();
    }

    protected void cancel() {
        dispose();
    }

    public abstract void getInfo();

    protected void getParamsData() {
        wParams.clearAll(false);
        ArrayList<String> paramNames = new ArrayList<String>(configuration.getParams().keySet());
        Collections.sort(paramNames);

        for (int i = 0; i < paramNames.size(); i++) {
            String paramName = paramNames.get(i);
            String paramValue = configuration.getParams().get(paramName);
            String defaultValue;
            try {
                defaultValue = abstractMeta.getParameterDefault(paramName);
            } catch (UnknownParamException e) {
                defaultValue = "";
            }

            String description;
            try {
                description = abstractMeta.getParameterDescription(paramName);
            } catch (UnknownParamException e) {
                description = "";
            }

            TableItem tableItem = new TableItem(wParams.table, SWT.NONE);
            tableItem.setText(1, paramName);
            tableItem.setText(2, Const.NVL(defaultValue, ""));
            tableItem.setText(3, Const.NVL(paramValue, ""));
            tableItem.setText(4, Const.NVL(description, ""));
        }
        wParams.removeEmptyRows();
        wParams.setRowNums();
        wParams.optWidth(true);
    }

    /**
     * @param configuration
     *            the configuration to set
     */

    public void setConfiguration(ExecutionConfiguration configuration) {
        this.configuration = configuration;
    }

    protected void mainLayout(Class<?> PKG, String prefix, Image img) {
        display = parent.getDisplay();
        shell = new Shell(parent, SWT.DIALOG_TRIM | SWT.MIN | SWT.APPLICATION_MODAL);
        props.setLook(shell);
        shell.setImage(img);
        shell.setLayout(new FormLayout());
        shell.setText(BaseMessages.getString(PKG, prefix + ".Shell.Title"));
    }

    /**
     * @param PKG
     * @param prefix
     */
    protected void optionsSectionLayout(Class<?> PKG, String prefix) {
        gDetails = new Group(shell, SWT.SHADOW_ETCHED_IN);
        gDetails.setText(BaseMessages.getString(PKG, prefix + ".DetailsGroup.Label"));
        props.setLook(gDetails);

        // The layout
        gDetails.setLayout(new FormLayout());
        fdDetails = new FormData();
        fdDetails.top = new FormAttachment(cRunConfiguration, 15);
        fdDetails.right = new FormAttachment(100, -15);
        fdDetails.left = new FormAttachment(0, 15);
        gDetails.setBackground(shell.getBackground()); // the default looks ugly
        gDetails.setLayoutData(fdDetails);

        optionsSectionControls();
    }

    /**
     * @param null
     * @param: *
     * @return:
     * @date: 2022-04-08 10:41
     */

    protected void parametersSectionLayout(Class<?> PKG, String prefix) {

        tabFolder = new CTabFolder(shell, SWT.BORDER);
        props.setLook(tabFolder, Props.WIDGET_STYLE_TAB);
        fd_tabFolder = new FormData();
        fd_tabFolder.right = new FormAttachment(100, -15);
        fd_tabFolder.left = new FormAttachment(0, 15);
        fd_tabFolder.top = new FormAttachment(gDetails, 15);
        fd_tabFolder.bottom = new FormAttachment(gDetails, 370);
        tabFolder.setLayoutData(fd_tabFolder);

        // Parameters
        CTabItem tbtmParameters = new CTabItem(tabFolder, SWT.NONE);
        tbtmParameters.setText(BaseMessages.getString(PKG, prefix + ".Params.Label"));
        Composite parametersComposite = new Composite(tabFolder, SWT.NONE);
        props.setLook(parametersComposite);

        parametersComposite.setLayout(new FormLayout());
        tbtmParameters.setControl(parametersComposite);

        ColumnInfo[] cParams = {
            new ColumnInfo(BaseMessages.getString(PKG, prefix + ".ParamsColumn.Argument"), ColumnInfo.COLUMN_TYPE_TEXT,
                false, true, 126), // Stepname
            new ColumnInfo(BaseMessages.getString(PKG, prefix + ".ParamsColumn.Default"), ColumnInfo.COLUMN_TYPE_TEXT,
                false, true, 138), // Preview
            // size
            new ColumnInfo(BaseMessages.getString(PKG, prefix + ".ParamsColumn.Value"), ColumnInfo.COLUMN_TYPE_TEXT,
                false, false, 142), // Preview size
            new ColumnInfo(BaseMessages.getString(PKG, prefix + ".ParamsColumn.Description"),
                ColumnInfo.COLUMN_TYPE_TEXT, false, true, 181), // Preview
            // size
        };

        String[] namedParams = abstractMeta.listParameters();
        int nrParams = namedParams.length;
        wParams = new TableView(abstractMeta, parametersComposite, SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI, cParams,
            nrParams, false, null, props, false);
        FormData fdParams = new FormData();
        fdParams.top = new FormAttachment(0, 10);
        fdParams.right = new FormAttachment(100, -10);
        fdParams.bottom = new FormAttachment(100, -45);
        fdParams.left = new FormAttachment(0, 10);
        wParams.setLayoutData(fdParams);

        tabFolder.setSelection(0);

        Button argsButton = new Button(parametersComposite, SWT.NONE);
        FormData fd_argsButton = new FormData();
        fd_argsButton.right = new FormAttachment(100, -10);
        fd_argsButton.top = new FormAttachment(wParams, 6);
        fd_argsButton.bottom = new FormAttachment(100, -10);
        argsButton.setLayoutData(fd_argsButton);
        argsButton.setText(BaseMessages.getString(PKG, prefix + ".Arguments.Label"));

        argsButton.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                new ArgumentsDialog(shell, configuration, abstractMeta);
            }
        });

        // Variables
        CTabItem tbtmVariables = new CTabItem(tabFolder, SWT.NONE);
        tbtmVariables.setText(BaseMessages.getString(PKG, prefix + ".Variables.Label"));

        Composite variablesComposite = new Composite(tabFolder, SWT.NONE);
        props.setLook(variablesComposite);
        variablesComposite.setLayout(new FormLayout());
        tbtmVariables.setControl(variablesComposite);

        ColumnInfo[] cVariables = {
            new ColumnInfo(BaseMessages.getString(PKG, prefix + ".VariablesColumn.Argument"),
                ColumnInfo.COLUMN_TYPE_TEXT, false, false, 287), // Stepname
            new ColumnInfo(BaseMessages.getString(PKG, prefix + ".VariablesColumn.Value"), ColumnInfo.COLUMN_TYPE_TEXT,
                false, false, 300), // Preview
            // size
        };

        int nrVariables = configuration.getVariables() != null ? configuration.getVariables().size() : 0;
        wVariables = new TableView(abstractMeta, variablesComposite, SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI,
            cVariables, nrVariables, false, null, props, false);

        FormData fdVariables = new FormData();
        fdVariables.top = new FormAttachment(0, 10);
        fdVariables.right = new FormAttachment(100, -10);
        fdVariables.bottom = new FormAttachment(100, -10);
        fdVariables.left = new FormAttachment(0, 10);

        wVariables.setLayoutData(fdVariables);
    }

    protected void buttonsSectionLayout(Class<?> PKG, String prefix, final String docTitle, final String docUrl,
        final String docHeader) {

        // Bottom buttons and separator

        alwaysShowOption = new Button(shell, SWT.CHECK);
        props.setLook(alwaysShowOption);
        alwaysShowOption.setSelection(abstractMeta.isAlwaysShowRunOptions());

        alwaysShowOption.setToolTipText(BaseMessages.getString(PKG, prefix + ".alwaysShowOption"));

        FormData fd_alwaysShowOption = new FormData();
        fd_alwaysShowOption.left = new FormAttachment(0, 15);
        fd_alwaysShowOption.top = new FormAttachment(tabFolder, 15);
        alwaysShowOption.setLayoutData(fd_alwaysShowOption);
        alwaysShowOption.setText(BaseMessages.getString(PKG, prefix + ".AlwaysOption.Value"));

        wCancel = new Button(shell, SWT.PUSH);
        FormData fd_wCancel = new FormData();
        fd_wCancel.bottom = new FormAttachment(100, -15);
        wCancel.setLayoutData(fd_wCancel);
        wCancel.setText(BaseMessages.getString(PKG, "System.Button.Cancel"));
        wCancel.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                cancel();
            }
        });

        wOK = new Button(shell, SWT.PUSH);
        FormData fd_wOK = new FormData();
        fd_wOK.top = new FormAttachment(wCancel, 0, SWT.TOP);
        fd_wOK.right = new FormAttachment(wCancel, -5);
        fd_wOK.bottom = new FormAttachment(100, -15);
        wOK.setLayoutData(fd_wOK);
        wOK.setText(BaseMessages.getString(PKG, prefix + ".Button.Launch"));
        wOK.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                ok();
            }
        });

        // 增加远程定时启动
        // this.PKG = PKG;
        // this.PREFIX = prefix;
        // wQuartzStart = new Button(shell, SWT.PUSH);
        // FormData fd_wQuartzStart = new FormData();
        // fd_wQuartzStart.top = new FormAttachment(wOK, 0, SWT.TOP);
        // fd_wQuartzStart.right = new FormAttachment(wOK, -5);
        // fd_wQuartzStart.bottom = new FormAttachment(100, -15);
        // wQuartzStart.setLayoutData(fd_wQuartzStart);
        // wQuartzStart.setText(BaseMessages.getString(PKG, prefix + ".Button.QuartzStart"));
        // wQuartzStart.setEnabled(false);
        // wQuartzStart.addSelectionListener(new SelectionAdapter() {
        // public void widgetSelected(SelectionEvent e) {
        // // 弹窗输入cron表达式,返回Cron表达式
        // setCron(showCronWriteText());
        // if(!"".equals(cron)&&cron!=null){
        // ok();
        // }
        // }
        // });

        Button btnHelp = new Button(shell, SWT.NONE);
        btnHelp.setImage(GUIResource.getInstance().getImageHelpWeb());
        btnHelp.setText(BaseMessages.getString(PKG, "System.Button.Help"));
        btnHelp.setToolTipText(BaseMessages.getString(PKG, "System.Tooltip.Help"));
        FormData fd_btnHelp = new FormData();
        fd_btnHelp.bottom = new FormAttachment(100, -15);
        fd_btnHelp.left = new FormAttachment(0, 15);
        btnHelp.setLayoutData(fd_btnHelp);
        btnHelp.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent evt) {
                HelpUtils.openHelpDialog(parent.getShell(), docTitle, docUrl, docHeader);
            }
        });

        Label separator = new Label(shell, SWT.SEPARATOR | SWT.HORIZONTAL);
        if (Const.isLinux()) {
            fd_wCancel.top = new FormAttachment(separator, 10);
        } else {
            fd_wCancel.top = new FormAttachment(separator, 15);
        }
        if (Const.isLinux()) {
            fd_btnHelp.top = new FormAttachment(separator, 10);
        } else {
            fd_btnHelp.top = new FormAttachment(separator, 15);
        }
        fd_wCancel.right = new FormAttachment(100, -15);
        FormData fd_separator = new FormData();
        fd_separator.right = new FormAttachment(100, -15);
        fd_separator.left = new FormAttachment(0, 15);
        fd_separator.top = new FormAttachment(alwaysShowOption, 15);
        separator.setLayoutData(fd_separator);
    }

    protected void openDialog() {
        shell.pack();
        // Set the focus on the OK button
        wOK.setFocus();

        Rectangle shellBounds = getParent().getBounds();
        Point dialogSize = shell.getSize();

        shell.setLocation(shellBounds.x + (shellBounds.width - dialogSize.x) / 2,
            shellBounds.y + (shellBounds.height - dialogSize.y) / 2);

        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
    }

    protected void runConfigurationSectionLayout(Class<?> PKG, String prefix) {
        cRunConfiguration = new Composite(shell, SWT.NONE);
        cRunConfiguration.setLayout(new FormLayout());
        props.setLook(cRunConfiguration);
        FormData fdLocal = new FormData();
        fdLocal.top = new FormAttachment(0, 15);
        fdLocal.right = new FormAttachment(100, -15);
        fdLocal.left = new FormAttachment(0, 15);

        cRunConfiguration.setBackground(shell.getBackground()); // the default
        // looks ugly
        cRunConfiguration.setLayoutData(fdLocal);

        Label wlRunConfiguration = new Label(cRunConfiguration, SWT.LEFT);
        props.setLook(wlRunConfiguration);
        wlRunConfiguration.setText(BaseMessages.getString(PKG, prefix + ".Configuration"));
        FormData fdlRunConfiguration = new FormData();
        fdlRunConfiguration.top = new FormAttachment(0);
        fdlRunConfiguration.left = new FormAttachment(0);
        wlRunConfiguration.setLayoutData(fdlRunConfiguration);

        wRunConfiguration = new CCombo(cRunConfiguration, SWT.BORDER);
        props.setLook(wRunConfiguration);
        FormData fdRunConfiguration = new FormData();
        fdRunConfiguration.width = 200;
        fdRunConfiguration.top = new FormAttachment(wlRunConfiguration, 5);
        fdRunConfiguration.left = new FormAttachment(0);
        wRunConfiguration.setLayoutData(fdRunConfiguration);
    }

    protected abstract void optionsSectionControls();

    /**
     * Cron Dialog内部类
     *
     * @author asus
     */
    class CronDialog extends org.eclipse.jface.dialogs.Dialog {
        private String textValue;
        private Text text;
        private DateTime date;
        private DateTime time;
        private static final int APPLY_ID = 101;

        protected CronDialog(Shell parentShell) {
            super(parentShell);
        }

        protected String getTextValue() {
            return textValue;
        }

        protected Date getTime() {
            Calendar calendar = Calendar.getInstance();
            calendar.set(this.date.getYear(), this.date.getMonth(), this.date.getDay(), this.time.getHours(),
                this.time.getMinutes(), this.time.getSeconds());
            return calendar.getTime();
        }

        protected Control createDialogArea(Composite parent) {
            Composite topComp = new Composite(parent, SWT.NONE);
            topComp.setLayout(new RowLayout());// 应用RowLayout面局
            date = new DateTime(topComp, SWT.DATE);
            time = new DateTime(topComp, SWT.TIME);
            text = new Text(topComp, SWT.BORDER);// 加入一个文本框
            text.setText(textValue == null ? "" : textValue);
            text.setLayoutData(new RowData(150, -1));// 用RowData来设置文本框的长度
            return topComp;
        }

        protected void initializeBounds() {
            Composite comp = (Composite)getButtonBar();
            super.createButton(comp, APPLY_ID, BaseMessages.getString(PKG, PREFIX + ".CronDialog.DialogButton"), true);
            super.initializeBounds();
        }

        /**
         * 单击对话框底部按钮会执行此方法，参数buttonId是被单击按钮的ID值。
         */
        protected void buttonPressed(int buttonId) {
            // 如果单击确定按钮，则将值保存到变量
            if (buttonId == IDialogConstants.OK_ID) {
                if ("".equals(text.getText())) {
                    MessageDialog.openError(shell, "", BaseMessages.getString(PKG, PREFIX + ".CronDialog.Error"));
                    return;
                } else {
                    textValue = text.getText();
                }
            }
            // 如果点击【生成Cron】按钮，则根据日期
            if (buttonId == CronDialog.APPLY_ID) {
                text.setText(formatDateByPattern(this.getTime()));
            }
            super.buttonPressed(buttonId);
        }
    }

    /**
     * 弹窗输入cron表达式
     */
    private String showCronWriteText() {
        cron = "";
        CronDialog dialog = new CronDialog(shell);
        dialog.open();
        return dialog.getTextValue();
    }

    /**
     * 将时间转为Cron表达式
     *
     * @param date
     * @return
     */
    private static String formatDateByPattern(Date date) {
        String dateFormat = "ss mm HH dd MM ? yyyy";
        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
        String formatTimeStr = null;
        if (date != null) {
            formatTimeStr = sdf.format(date);
        }
        return formatTimeStr;
    }

    /**
     * 获得Cron表达式
     *
     * @return
     */
    public String getCron() {
        return cron;
    }

    public void setCron(String cron) {
        this.cron = cron;
    }
}
